package project.messagestack.network;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.URI;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

import project.messagestack.utils.Logger;

public class MailTransport implements Transporter {
  
  private String mHost;
  private int mPort;
  private Socket mSocket;
  private OutputStream mOut;
  private InputStream mIn;
  private String[] mUserAuthInfo;
  private boolean mSecurity;
  private static int CONNECT_TIMEOUT = 10000;
  
  public void open() {
    try {
      SocketAddress sockAddr = new InetSocketAddress(getHost(), getPort());
      if (useSecurity()) {
        mSocket = SSLSocketFactory.getDefault().createSocket();
      } else {
        mSocket = new Socket();
      }
      mSocket.connect(sockAddr, CONNECT_TIMEOUT);
      Logger.logInfo("connected " + mSocket.isConnected());
      if (useSecurity()) {
        SSLSocket s = (SSLSocket) mSocket;
        s.startHandshake();
        SSLSession session = s.getSession();
        if (session == null) {
          throw new SSLException("Cannot verify SSL socket without session");
        }
        if (!HttpsURLConnection.getDefaultHostnameVerifier().verify(getHost(),
                                                                    session)) {
          throw new SSLPeerUnverifiedException(
              "Certificate hostname not useable for server: " + getHost());
        }
      }
      mIn = new BufferedInputStream(mSocket.getInputStream(), 2048);
      mOut = new BufferedOutputStream(mSocket.getOutputStream(), 1024);
    } catch (IOException e) {
      Logger.logError(e.getMessage());
    }
  }

  public void setUri(URI uri, int port) {
    mHost = uri.getHost();
    mPort = port;
    if (uri.getPort() != -1) {
      mPort = uri.getPort();
    }
    if (uri.getUserInfo() != null) {
      mUserAuthInfo = uri.getUserInfo().split(":", 1);
    }
  }
  
  public void close() {
    try {
      mIn.close();
    } catch (IOException e) { }
    try {
      mOut.close();
    } catch (IOException e) { }
    try {
      mSocket.close();
    } catch (IOException e) { }
    mIn = null;
    mOut = null;
    mSocket = null;
  }

  public String getHost() {
    return mHost;
  }

  public InputStream getInputStream() {
    return mIn;
  }

  public OutputStream getOutputStream() {
    return mOut;
  }

  public int getPort() {
    return mPort;
  }

  public String[] getUserAuthInfo() {
    return mUserAuthInfo;
  }
  
  public boolean isOpen() {
    return (mSocket != null && mIn != null && mOut != null && 
            mSocket.isConnected() && !mSocket.isClosed());
  }

  public String readLine() throws IOException {
    if (!isOpen()){
      Logger.logInfo("Readline on closed connection");
      return null;
    }
    StringBuffer sb = new StringBuffer();
    int c;
    InputStream is = getInputStream();
    while ((c = is.read()) != -1) {
      if (c == '\r') {
        continue;
      } else if (c == '\n') {
        break;
      } else {
        sb.append((char)c);
      }
    }
    return sb.toString();
  }

  public void setSoTimeout(int miliseconds) throws SocketException {
    mSocket.setSoTimeout(miliseconds);
  }
  
  public boolean useSecurity() {
    return mSecurity;
  }
  
  public void setSecurity(boolean useSecurity) {
    mSecurity = useSecurity;
  }

  public void writeLine(String message) throws IOException {
    OutputStream out = getOutputStream();
    out.write(message.trim().getBytes()); // trim just to make sure
    out.write('\r');
    out.write('\n');
    out.flush();
  }

}
